home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
admin
/
login
/
sac-1.000
/
sac-1
/
sac-1.3.1
/
sac.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-05-27
|
28KB
|
1,129 lines
/* $Copyright: $
* Copyright (c) 1995, 1996 by Steve Baker (ice@mama.indstate.edu)
* All Rights reserved
*
* This software is provided as is without any express or implied
* warranties, including, without limitation, the implied warranties
* of merchant-ability and fitness for a particular purpose.
*/
#include <utmp.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <paths.h>
#include <string.h>
static char *version = "$Version: $ sac v1.3.1 (c) 1995, 1996 by Steve Baker $";
/* FSSTND compliance courtesy of Edward S. Marshall */
#ifndef _PATH_WTMP
#define _PATH_WTMP "/var/log/wtmp" /* FSSTND compliant */
#endif
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
enum {
TOTAL = 0x0001, /* Total login time */
DAY = 0x0002, /* Daily average */
USER = 0x0003, /* Per user login time */
AVERAGE = 0x0010, /* Average login time / login */
HOUR = 0x0020, /* Hourly profile */
CLIP = 0x0040, /* Multiple logins during same time count only once */
FTP = 0x0080, /* Perform ftp accounting only. */
BOTH = 0x0100, /* Perform both normal and ftp accounting. */
MINMAX = 0x0200 /* Show min max # logins at one time */
};
enum { FALSE=0, TRUE=1 };
enum { USERLIST, EXCLUDELIST, TTYLIST, HOSTLIST };
struct day *mkday();
struct usr *adduser(), *finduser();
void *malloc(), *realloc(), *malloc();
time_t getdate(), churn_time();
struct day {
time_t start, stop;
short day, month, year, minlogins, maxlogins;
time_t time;
time_t h[24];
u_long logins;
struct usr *us;
struct day *nxt;
} *days = NULL, *end=NULL;
/*
* Keep a pointer at the end of the days list, since we'll usually be
* adding crap to it, not to days before. What 'o what do we do about
* time changes aka time warps?
*/
struct usr {
char user[UT_NAMESIZE+1];
time_t time;
time_t h[24];
u_long logins, xlogins;
struct usr *nxt;
} *us = NULL;
struct exc {
char user[UT_NAMESIZE+1];
struct exc *nxt;
} *ex = NULL;
struct ttys {
char line[UT_LINESIZE+1];
struct ttys *nxt;
} *tty = NULL;
struct hosts {
char host[UT_HOSTSIZE+1];
char len,ss; /* Is host a substring? */
struct hosts *nxt;
} *hosts = NULL;
struct user {
char user[UT_NAMESIZE+1];
char line[UT_LINESIZE+1];
char host[UT_HOSTSIZE+1];
time_t in;
struct user *nxt;
} *usr = NULL;
struct tacacs_utmp {
char ut_line[8];
char ut_user[8];
char ut_host[16];
time_t ut_time;
};
char fix = FALSE, exclude = FALSE, fixtty = FALSE, fixhost = FALSE;
char *back = NULL;
u_short type = TOTAL;
int fd;
time_t total = 0, sd = 0, ed = 0, backtime = 0, curtime = 0;
int ndays = 0, logins = 0, loggedin = 0, minlogins = -1, maxlogins = 0;
signed int sm = 0, em = 0;
/*
* sac [-w wtmp|-] [-dahpcotfFm] [-b hour[:min[:sec]]] [-s start] [-e end]
* [[-u] users] [-x [users]] [-T [ttylist]] [-H [hostlist]]
*/
main(argc,argv)
int argc;
char **argv;
{
char *file = _PATH_WTMP, *start = NULL, *end = NULL, old = FALSE;
char tacacs = FALSE, ftp = FALSE, both = FALSE;
char listtype = USERLIST;
int i, j, n;
time_t t, s, e;
for(n=i=1;i<argc;i=n) {
n++;
if (argv[i][0] == '-') {
for(j=1;argv[i][j];j++) {
switch (argv[i][j]) {
case 'w':
file = argv[n++];
break;
case 'p':
type = USER | (type & 0xFFF0);
break;
case 'd':
type = DAY | (type & 0xFFF0);
break;
case 'a':
type |= AVERAGE;
break;
case 'c':
type |= CLIP;
break;
case 'h':
type |= HOUR;
break;
case 'm':
type |= MINMAX;
break;
case 'F':
type |= FTP;
ftp = TRUE;
break;
case 'f':
type |= BOTH;
both = TRUE;
break;
case 'o':
old = TRUE;
break;
case 't':
tacacs = TRUE;
break;
case 'b':
back = argv[n++];
break;
case 's':
start = argv[n++];
break;
case 'e':
end = argv[n++];
break;
case 'x':
listtype = EXCLUDELIST;
break;
case 'T':
listtype = TTYLIST;
break;
case 'u':
listtype = USERLIST;
break;
case 'H':
listtype = HOSTLIST;
break;
case '-':
if (j == 1) {
if (!strcmp("--help",argv[i])) usage(4);
}
default:
usage(1);
}
}
} else {
switch(listtype) {
case USERLIST:
adduser(argv[i]);
fix = TRUE;
break;
case EXCLUDELIST:
addexclude(argv[i]);
exclude = TRUE;
break;
case TTYLIST:
addtty(argv[i]);
fixtty = TRUE;
break;
case HOSTLIST:
addhost(argv[i]);
fixhost = TRUE;
break;
}
}
}
if (start) {
if (start[0] == '-' || start[0] == '+') {
for(i=1;start[i];i++) if (!isdigit(start[i])) usage(2);
sm = atoi(start);
} else sd = getdate(start);
}
if (end) {
if (end[0] == '-' || end[0] == '+') {
t = time(0);
for(i=1;end[i];i++) if (!isdigit(end[i])) usage(2);
em = atoi(end);
} else ed = getdate(end);
}
if (back) backtime = (curtime=time(0)) - churn_time(back);
if (!strcmp(file,"-")) fd = 0;
else fd = open(file,O_RDONLY);
if (both) doitboth();
else if (ftp) doitftp();
else if (tacacs) doittacacs();
else if (old) doitold();
else doit();
cleanup();
report();
close(fd);
}
usage(n)
int n;
{
switch (n) {
case 1:
fprintf(stderr,"usage: sac [-w wtmp] [-dpfFahcotm] [-s start] [-e end] [-b H[:M[:S]]]\n [[-u] userlist] [-x [userlist]] [-T [ttylist]] [-H [hostlist]]\n");
break;
case 2:
fprintf(stderr,"sac: Invalid date. Format: +days | -days | mm/dd/yy\n");
break;
case 3:
fprintf(stderr,"sac: Invalid time. Format: hours[:minutes[:seconds]]\n");
break;
case 4:
fprintf(stderr,"usage: sac [-w wtmp] [-dpfFahcotm] [-s start] [-e end] [-b H[:M[:S]]]\n [[-u] userlist] [-x [userlist]] [-T [ttylist]] [-H [hostlist]]\n");
fprintf(stderr," -w wtmp Read alternate wtmp file.\n");
fprintf(stderr," -d Perform daily accounting.\n");
fprintf(stderr," -a Average usage / login.\n");
fprintf(stderr," -h Show hourly profile.\n");
fprintf(stderr," -p Per user accounting.\n");
fprintf(stderr," -f Perform accounting for ftp logins too.\n");
fprintf(stderr," -F Perform accounting for ftp logins only.\n");
fprintf(stderr," -m Show min/max # of concurrent logins.\n");
fprintf(stderr," -c Perform login clipping.\n");
fprintf(stderr," -o Read old decrepit wtmp format.\n");
fprintf(stderr," -t Read tacacs wtmp format.\n");
fprintf(stderr," -s start Display accounting info from `start'.\n");
fprintf(stderr," -e end Display accounting info up to `end'.\n");
fprintf(stderr," -b H:M:S Show accounting info from the last few hours:minutes:seconds.\n");
fprintf(stderr," -x [users] Does not perform accounting for [users].\n");
fprintf(stderr," -T [ttys] Perform accounting on only those ttys listed.\n");
fprintf(stderr," -H [hosts] Perform accounting only for the hosts listed.\n");
fprintf(stderr," -u [users] Perform accounting only for the users listed.\n");
fprintf(stderr," userlist Perform accounting only for the users listed.\n");
break;
}
exit(1);
}
/*
* Turn "mm/dd/yy" into time in seconds.
*/
time_t getdate(s)
char *s;
{
struct tm tm;
int y;
time_t t;
/*
* <puke>
* Need a real date parser that can handle different formats and separators
*/
if (!isdigit(s[0]) || !isdigit(s[1]) || isdigit(s[2])) usage(2);
if (!isdigit(s[3]) || !isdigit(s[4]) || isdigit(s[5])) usage(2);
if (!isdigit(s[6]) || !isdigit(s[7]) || s[8]) usage(2);
tm.tm_mon = (((s[0]-'0')*10) + (s[1]-'0')) -1;
tm.tm_mday = (((s[3]-'0')*10) + (s[4]-'0'));
y = (((s[6]-'0')*10) + (s[7]-'0'));
tm.tm_year = (y < 70? 100 + y : y);
tm.tm_isdst = -1;
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
t = mktime(&tm);
if (t == (time_t)(-1)) usage(2);
return t;
}
time_t churn_time(s)
char *s;
{
time_t t = 0, mult=3600;
char nbuf[20], p;
for(p=0;*s;s++) {
if (*s == ':') {
nbuf[p] = 0;
t += atoi(nbuf) * mult;
p = 0;
if (mult > 1) mult /= 60;
else usage(3);
} else if (isdigit(*s)) {
if (p < 15) nbuf[p++] = *s;
else usage(3);
} else usage(3);
}
nbuf[p] = 0;
t += atoi(nbuf) * mult;
return t;
}
doit()
{
struct utmp u;
void *ut = &u;
int n, m;
while (1) {
for(n=m=read(fd,ut,sizeof(struct utmp));n < sizeof(struct utmp);n+=m=read(fd,ut+n,sizeof(struct utmp)-n)) {
if (m < 0) {
perror("sac");
return;
}
if (!m) return;
}
if (u.ut_time == 0) continue; /* corrupted wtmp entry! */
checkday(u); /* Do we have this day allocated? */
/* Q: Why does the following bother me? */
/* A: It may not handle all possible cases. Wtmp documentation sucks.
* Programs are also pretty free to put whatever the hell they want
* in wtmp.
*/
if (u.ut_line[0]) {
if (u.ut_user[0]) {
if (!strcmp("~",u.ut_line) && (!strcmp("reboot",u.ut_user) || !strcmp("shutdown",u.ut_user))) do_reboot(u);
else if (u.ut_type == USER_PROCESS && strncmp("ftp",u.ut_line,3)) login(u);
else if (u.ut_type == DEAD_PROCESS && strncmp("ftp",u.ut_line,3)) logout(u,0);
else if (u.ut_type == LOGIN_PROCESS) logout(u,0);
} else {
if (!strcmp("|",u.ut_line) || !strcmp("{",u.ut_line)) changetime(u);
else logout(u,0);
}
}
}
}
/*
* Do it the old way, where u.ut_type is invalid or doesn't get used.
* This is for non-standard wtmp's kept by programs such as tacacs.
* Note: Xterm writes a wtmp entry on exit that looks like a login entry
* if we ignore the ut_type field (which is "dead process" in this
* case). This will obviously screw things up.
*
* This may not be 100%.
*/
doitold()
{
struct utmp u;
void *ut = &u;
int n, m;
while (1) {
for(n=m=read(fd,ut,sizeof(struct utmp));n < sizeof(struct utmp);n+=m=read(fd,ut+n,sizeof(struct utmp)-n)) {
if (m < 0) {
perror("sac");
return;
}
if (!m) return;
}
if (u.ut_time == 0) continue; /* corrupted wtmp entry! */
checkday(u);
if (u.ut_line[0]) {
if (u.ut_user[0]) {
if (!strcmp("~",u.ut_line) && (!strcmp("reboot",u.ut_user) || !strcmp("shutdown",u.ut_user))) do_reboot(u);
else if (strcmp("LOGIN",u.ut_user) && strcmp("~",u.ut_line) && strncmp("ftp",u.ut_line,3)) login(u);
else logout(u,0);
} else {
if (!strcmp("|",u.ut_line) || !strcmp("{",u.ut_line)) changetime(u);
else logout(u,0);
}
}
}
}
/*
* Do it the tacacs way, with tacacs ancient utmp format, which may work for
* other ancient programs.
* This too may not be 100%.
*/
doittacacs()
{
struct tacacs_utmp t;
struct utmp u;
void *ut = &u;
int n, m;
while (1) {
for(n=m=read(fd,ut,sizeof(struct utmp));n < sizeof(struct utmp);n+=m=read(fd,ut+n,sizeof(struct utmp)-n)) {
if (m < 0) {
perror("sac");
return;
}
if (!m) return;
}
if (t.ut_time == 0) continue; /* corrupted wtmp entry! */
/* put tacacs_utmp into a normal utmp for the rest of this */
u.ut_time = t.ut_time;
strncpy(u.ut_line,t.ut_line,8);
strncpy(u.ut_user,t.ut_user,8);
u.ut_line[8] = u.ut_user[8] = 0;
checkday(u);
if (u.ut_line[0]) {
if (u.ut_user[0]) {
if (!strcmp("~",u.ut_line) && (!strcmp("reboot",u.ut_user) || !strcmp("shutdown",u.ut_user))) do_reboot(u);
else if (strcmp("LOGIN",u.ut_user) && strcmp("~",u.ut_line) && strncmp("ftp",u.ut_line,3)) login(u);
else logout(u,0);
} else {
if (!strcmp("|",u.ut_line) || !strcmp("{",u.ut_line)) changetime(u);
else logout(u,0);
}
}
}
}
/*
* Perform accounting on ftp logins only. This handles ftp entries generated
* from wu-ftpd at least.
*/
doitftp()
{
struct utmp u;
void *ut = &u;
int n, m;
while (1) {
for(n=m=read(fd,ut,sizeof(struct utmp));n < sizeof(struct utmp);n+=m=read(fd,ut+n,sizeof(struct utmp)-n)) {
if (m < 0) {
perror("sac");
return;
}
if (!m) return;
}
if (u.ut_time == 0) continue; /* corrupted wtmp entry! */
checkday(u); /* Do we have this day allocated? */
if (u.ut_line[0]) {
if (u.ut_user[0]) {
if (!strcmp("~",u.ut_line) && (!strcmp("reboot",u.ut_user) || !strcmp("shutdown",u.ut_user))) do_reboot(u);
else if (!strncmp("ftp",u.ut_line,3)) login(u);
} else {
if (!strcmp("|",u.ut_line) || !strcmp("{",u.ut_line)) changetime(u);
else if (!strncmp("ftp",u.ut_line,3)) logout(u,0);
}
}
}
}
/*
* Perform accounting on both normal logins and ftp simultaneously.
*/
doitboth()
{
struct utmp u;
void *ut = &u;
int n, m;
while (1) {
for(n=m=read(fd,ut,sizeof(struct utmp));n < sizeof(struct utmp);n+=m=read(fd,ut+n,sizeof(struct utmp)-n)) {
if (m < 0) {
perror("sac");
return;
}
if (!m) return;
}
if (u.ut_time == 0) continue; /* corrupted wtmp entry! */
checkday(u); /* Do we have this day allocated? */
if (u.ut_line[0]) {
if (u.ut_user[0]) {
if (!strcmp("~",u.ut_line) && (!strcmp("reboot",u.ut_user) || !strcmp("shutdown",u.ut_user))) do_reboot(u);
else if (u.ut_type == USER_PROCESS || !strncmp("ftp",u.ut_line,3)) login(u);
else if (u.ut_type == DEAD_PROCESS) logout(u,0);
else if (u.ut_type == LOGIN_PROCESS) logout(u,0);
} else {
if (!strcmp("|",u.ut_line) || !strcmp("{",u.ut_line)) changetime(u);
else logout(u,0);
}
}
}
}
/*
* Make sure the day that the wtmp entry corresponds to, exists in our
* days list. If day is less than the starting day by more than a day, or
* greater than the last day by more than a day, allocate all the days in
* between as well.
*/
checkday(u)
struct utmp u;
{
struct day *d, *p;
if (days == NULL) end = days = mkday(u.ut_time);
else {
if (u.ut_time < days->start) {
p = d = mkday(u.ut_time);
while (p->stop+1 < days->start) {
p->nxt = mkday(p->stop+1);
p = p->nxt;
}
p->nxt = days;
days = d;
} else if (u.ut_time > end->stop) {
p = d = mkday(end->stop+1);
while (p->stop < u.ut_time) {
p->nxt = mkday(p->stop+1);
p = p->nxt;
}
end->nxt = d;
end = p;
}
}
}
/*
* Makes a day entry. We'll assume a day is exactly 24 hours or 86400
* seconds long. I don't know if this is the case or not. I don't think
* the time algorithm takes into account leap seconds.
*/
struct day *mkday(t)
time_t t;
{
struct day *d;
struct tm *tm;
int i;
d = malloc(sizeof(struct day));
tm = localtime(&t);
tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
d->start = mktime(tm);
d->stop = d->start + 86399;
d->nxt = NULL;
d->us = NULL;
d->day = tm->tm_mday;
d->month = tm->tm_mon;
d->year = tm->tm_year;
d->logins = d->time = d->maxlogins = 0;
d->minlogins = -1;
for(i=0;i<24;i++) d->h[i] = 0;
ndays++;
return d;
}
login(u)
struct utmp u;
{
struct user *q;
struct day *d;
int l;
/*
* If we still have a login on this line, it's logged out for sure now!
* Wtmp could be corrupted.
*/
logout(u,1);
q = malloc(sizeof(struct user));
strncpy(q->line,u.ut_line,UT_LINESIZE);
strncpy(q->user,u.ut_user,UT_NAMESIZE);
strncpy(q->host,u.ut_host,UT_HOSTSIZE);
q->user[UT_NAMESIZE] = q->line[UT_LINESIZE] = q->host[UT_HOSTSIZE] = 0;
q->in = u.ut_time;
q->nxt = usr;
usr = q;
logins++;
if ((type & MINMAX) == MINMAX && !(fix && (finduser(us,q->user)) == NULL) && !(exclude && isexcluded(q->user)) && !(fixtty && !istty(q->line)) && !(fixhost && !ishost(q->host))) {
l = loggedin++;
maxlogins = max(maxlogins,loggedin);
if (minlogins > -1) minlogins = min(minlogins,l);
else minlogins = l;
if (u.ut_time < end->start) {
for(d=days;d;d=d->nxt) {
if (u.ut_time >= d->start && u.ut_time <= d->stop) {
d->maxlogins = max(d->maxlogins,loggedin);
if (d->minlogins > -1) d->minlogins = min(d->minlogins,l);
else d->minlogins = l;
break;
}
}
} else {
end->maxlogins = max(end->maxlogins,loggedin);
if (end->minlogins > -1) end->minlogins = min(end->minlogins,l);
else end->minlogins = l;
}
}
}
logout(u,f)
struct utmp u;
char f;
{
struct user *p, *q, ux;
struct day *d;
for(p=q=usr;p;) {
if (!strcmp(u.ut_line,p->line)) {
release(p,u.ut_time);
if ((type & MINMAX) == MINMAX && !(fix && (finduser(us,p->user)) == NULL) && !(exclude && isexcluded(p->user)) && !(fixtty && !istty(p->line)) && !(fixhost && !ishost(p->host))) {
loggedin = max(0,loggedin-1);
if (minlogins > -1) minlogins = min(minlogins,loggedin);
else minlogins = loggedin;
if (u.ut_time < end->start) {
for(d=days;d;d=d->nxt) {
if (u.ut_time >= d->start && u.ut_time <= d->stop) {
if (d->minlogins > -1) d->minlogins = min(d->minlogins,loggedin);
else d->minlogins = loggedin;
break;
}
}
} else {
if (end->minlogins > -1) end->minlogins = min(end->minlogins,loggedin);
else end->minlogins = loggedin;
}
}
if (p == usr) {
usr = p->nxt;
free(p);
p = q = usr;
} else {
q->nxt = p->nxt;
free(p);
p = q->nxt;
}
continue;
}
q = p;
p = p->nxt;
}
}
/*
* logout everyone on reboots or crashes.
*/
do_reboot(u)
struct utmp u;
{
struct user *p, *q;
struct day *d;
for(p=usr;p;) {
release(p,u.ut_time);
q = p;
p=p->nxt;
free(q);
}
usr = NULL;
if ((type & MINMAX) == MINMAX) {
loggedin = 0;
minlogins = 0;
if (u.ut_time < end->start) {
for(d=days;d;d=d->nxt)
if (u.ut_time >= d->start && u.ut_time <= d->stop) {
d->minlogins = 0;
break;
}
} else end->minlogins = 0;
}
}
/*
* A utmp entry denoted with a line of "|" denotes the "old" time before
* netdate updated the time. An entry with line of "{" denotes the new time.
* The difference in time is applied to the login time of every user still
* logged in.
*/
changetime(u)
struct utmp u;
{
static time_t old;
struct user *p;
signed long dif;
if (!strcmp("|",u.ut_line)) {
old = u.ut_time;
return;
}
dif = (signed long)(u.ut_time - old);
for(p=usr;p;p=p->nxt) p->in -= dif;
}
/*
* Apply login time for users who haven't logged out yet (or that the wtmp file
* in particular indicates haven't logged out by the EOF) to the days that
* are in days list. Login time is not applied to days not listed in the
* wtmp file (therefor all the days between the first and last wtmp entries).
* The reason for this is that if you're inspecting an old wtmp file, the
* wtmp may not indicate all logouts for the last day. It is not possible
* to know when the wtmp file really truly ends however, so we apply the
* minimum of the current time or the ending time for the last day.
*/
cleanup()
{
time_t t = time(0);
/*
* Oops, if we're clipping, we have to remove the released time from the
* list. This is pretty easily done, by moving usr, instead of using a
* temporary pointer.
*/
for(;usr;usr=usr->nxt)
release(usr,t);
}
/*
* Release a login entry, applying the login time to the particular day
* entries.
* A user is logged in on a particular day when:
* in >= start && in <= stop ||
* out >= start && out <= stop ||
* in < start && out > stop
*/
release(u,t)
struct user *u;
time_t t;
{
struct day *p;
struct usr *up;
struct user *q;
signed long tx;
int i;
if ((signed long)((t - u->in) + 1) < 0) return;
/*
* Clipping assumes that time moves forward. Only in computers is this
* not necessarily a safe assumption.
* Clipping rules:
* If (login time < all other login times)
* reduce logout time (t) to least login time (-1).
* else clip entirely.
*/
if ((type & CLIP) == CLIP) {
for(q=usr;q;q=q->nxt) {
if (q != u && !strcmp(u->user,q->user)) {
/* throw it out if he's already logged in earlier */
if (q->in <= u->in) return;
/* reduce the logout time to the lowest login time. */
if (q->in < t) t = q->in-1;
}
}
}
if (backtime) {
if (u->in > curtime || t < backtime) return;
if (u->in < backtime && t > backtime) u->in = backtime;
if (u->in < curtime && t > curtime) t = curtime;
}
/*
* If we've fixed the user list, then we only want to apply the login time
* if that user is in our user list.
*/
if (fix && (up = finduser(us,u->user)) == NULL) return;
/*
* Check the exclude user list.
*/
if (exclude && isexcluded(u->user)) return;
/*
* Check the tty list.
*/
if (fixtty && !istty(u->line)) return;
/*
* Check the host list.
*/
if (fixhost && !ishost(u->host)) return;
/* if we're logging usage / user then apply login time to user entry. */
if ((type & 0x000F) == USER && (((up = finduser(us,u->user)) != NULL) || !fix)) {
if (up == NULL) up = adduser(u->user);
if (u->in < end->start) {
for(p=days;p;p=p->nxt) {
if (u->in >= p->start && u->in <= p->stop) user_apply_hours(u,t,p);
else if (t >= p->start && t <= p->stop) user_apply_hours(u,t,p);
else if (u->in < p->start && t > p->stop) user_apply_hours(u,t,p);
}
} else user_apply_hours(u,t,end);
return;
}
/*
* We go through this for both daily and total. The total will be accumulated
* at the end since we don't know the starting and ending dates until we're
* done.
*/
if (u->in < end->start) {
/* Ugh, it's probably yesterday, but we've got to start all the way at the
* beginning. I wonder if double-linking the list would speed things up.
*/
for(p=days;p;p=p->nxt) {
if (u->in >= p->start && u->in <= p->stop) {
p->time += (min(p->stop,t) - u->in) + 1;
p->logins++;
if ((type & HOUR) == HOUR) apply_hours(u->in,t,p->start,p->h);
} else if (t >= p->start && t <= p->stop) {
p->time += (t - max(p->start,u->in)) + 1;
p->logins++;
if ((type & HOUR) == HOUR) apply_hours(u->in,t,p->start,p->h);
} else if (u->in < p->start && t > p->stop) {
p->time += 86400;
p->logins++;
if ((type & HOUR) == HOUR)
for(i=0;i<24;i++) p->h[i] += 3600;
}
}
} else {
end->time += (min(end->stop,t) - max(end->start,u->in)) + 1;
end->logins++;
if ((type & HOUR) == HOUR) apply_hours(u->in,t,end->start,end->h);
}
}
apply_hours(in,out,start,h)
time_t in, out, start, h[24];
{
int i;
time_t b, e;
b = start;
e = start + 3599;
for(i=0;i<24;i++) {
if (in >= b && in <= e) h[i] += (min(e,out) - in) + 1;
else if (out >= b && out <= e) h[i] += (out - max(b,in)) + 1;
else if (in < b && out > e) h[i] += 3600;
b += 3600;
e += 3600;
}
}
user_apply_hours(u,out,d)
struct user *u;
time_t out;
struct day *d;
{
int i;
time_t b, e;
struct usr *up;
if ((up = finduser(d->us,u->user)) == NULL) {
up = malloc(sizeof(struct usr));
bzero(up,sizeof(struct usr));
strncpy(up->user,u->user,UT_NAMESIZE);
up->nxt = d->us;
d->us = up;
}
up->time += (min(out,d->stop) - max(u->in,d->start)) + 1;
if (max(u->in,d->start) == u->in) up->logins++;
else up->xlogins++;
if ((type & HOUR) == HOUR) {
b = d->start;
e = d->start + 3599;
for(i=0;i<24;i++) {
if (u->in >= b && u->in <= e) up->h[i] += (min(e,out) - u->in) + 1;
else if (out >= b && out <= e) up->h[i] += (out - max(b,u->in)) + 1;
else if (u->in < b && out > e) up->h[i] += 3600;
b += 3600;
e += 3600;
}
}
}
struct usr *adduser(s)
char *s;
{
struct usr *u;
u = malloc(sizeof(struct usr));
bzero(u,sizeof(struct usr));
strncpy(u->user,s,UT_NAMESIZE);
u->nxt = us;
us = u;
return us;
}
struct usr *finduser(up,s)
struct usr *up;
char *s;
{
struct usr *u;
for(u=up;u;u=u->nxt)
if (!strcmp(s,u->user)) return u;
return NULL;
}
addexclude(s)
char *s;
{
struct exc *u;
u = malloc(sizeof(struct exc));
strncpy(u->user,s,UT_NAMESIZE);
u->user[UT_NAMESIZE] = 0;
u->nxt = ex;
ex = u;
return;
}
int isexcluded(s)
char *s;
{
struct exc *u;
for(u=ex;u;u=u->nxt)
if (!strcmp(s,u->user)) return TRUE;
return FALSE;
}
addtty(s)
char *s;
{
struct ttys *t;
t = malloc(sizeof(struct ttys));
strncpy(t->line,s,UT_LINESIZE);
t->line[UT_LINESIZE] = 0;
t->nxt = tty;
tty = t;
return;
}
int istty(s)
char *s;
{
struct ttys *t;
for(t=tty;t;t=t->nxt)
if (!strcmp(s,t->line)) return TRUE;
return FALSE;
}
addhost(s)
char *s;
{
struct hosts *h;
h = malloc(sizeof(struct hosts));
strncpy(h->host,s,UT_HOSTSIZE);
h->host[UT_HOSTSIZE] = 0;
h->len = strlen(h->host);
if ((index(h->host,'.') == NULL) || (h->host[h->len-1] == '.')) h->ss = TRUE;
h->nxt = hosts;
hosts = h;
return;
}
int ishost(s)
char *s;
{
struct hosts *h;
for(h=hosts;h;h=h->nxt) {
if (h->ss) if (!strncmp(s,h->host,h->len)) return TRUE;
else if (!strcmp(s,h->host)) return TRUE;
}
return FALSE;
}
report()
{
static char *month[] = {
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
};
struct day *d;
struct usr *u, *up;
time_t h[24];
int i, nl;
/*
* Narrow down the days list to the range of days specified by the -s and -e
* options. This seems kludged in to me, but I really can't see how else to
* do it.
*/
if (sd || ed || sm || em) {
if (sm < 0) sd = end->start + (86400*sm);
else if (sm > 0) sd = days->start + (86400*sm);
else if (!sd) sd = days->start;
if (em < 0) ed = end->start + (86400*em);
else if (em > 0) ed = days->start + (86400*em);
else if (!ed) ed = end->start;
if (sd > ed) exit(0);
for(d=days;d;d=d->nxt) {
if (d->start == sd) days = d;
if (d->start == ed) d->nxt = NULL;
}
}
/* Produce the reports... */
switch(type & 0x0f) {
case TOTAL:
for(ndays=total=0,d=days;d;d=d->nxt) {
total += d->time;
ndays++;
}
if (backtime) printf("Total: %12.2f over %s hours.\n", (float)total/3600, back);
else printf("Total: %12.2f over %d days.\n", (float)total/3600, ndays);
if ((type & AVERAGE) == AVERAGE) printf("Average: %10.2f / day, %10.2f / login\n",((float)total/3600) / ndays,((float)total/3600) / max(1,logins));
if ((type & MINMAX) == MINMAX) printf("Logins: %11d Min: %3d Max: %3d\n",logins, max(0,minlogins), maxlogins);
if ((type & HOUR) == HOUR) {
for(i=0;i<24;i++) h[i] = 0;
for(d=days;d;d=d->nxt)
for(i=0;i<24;i++) h[i] += d->h[i];
print_hours(h,total);
}
break;
case DAY:
for(d=days;d;d=d->nxt) {
printf("%s %2d total %10.2f",month[d->month],d->day,(float)d->time/3600);
if ((type & AVERAGE) == AVERAGE) {
if (d->logins)
printf(" %5d logins, %8.2f hrs/login",d->logins,((float)d->time/3600)/d->logins);
else {
printf(" no logins");
if ((type & MINMAX) == MINMAX) printf(" ");
}
}
if ((type & MINMAX) == MINMAX) {
if (d->minlogins == -1) d->maxlogins = d->minlogins = d->logins;
printf(" Min: %-3d Max: %d",d->minlogins, d->maxlogins);
}
putchar('\n');
if ((type & HOUR) == HOUR) print_hours(d->h,d->time);
}
break;
case USER:
for(u=us;u;u=u->nxt) {
for(d=days;d;d=d->nxt) {
if ((up = finduser(d->us,u->user)) != NULL) {
u->time += up->time;
u->logins += up->logins;
if (d == days) u->logins += up->xlogins;
if ((type & HOUR) == HOUR) for(i=0;i<24;i++) u->h[i] += up->h[i];
}
}
printf("\t%-8s %10.2f",u->user,(float)u->time/3600);
if ((type & AVERAGE) == AVERAGE) {
if (u->logins)
printf("\t%5d logins, %10.2f hours / login.",u->logins,((float)u->time/3600) / u->logins);
else
printf("\t no logins");
}
putchar('\n');
if ((type & HOUR) == HOUR) print_hours(u->h,u->time);
}
break;
}
}
print_hours(h,total)
time_t h[24], total;
{
static char *bar = "########################################################################";
int i, bl = strlen(bar);
float p[24], scale, maxp;
if (!total) {
for(i=0;i<24;i++)
printf("%02d-: \n",i);
} else {
for(i=0;i<24;i++) {
p[i] = (float)h[i] / (float)total;
if (p[i] > maxp) maxp = p[i];
}
scale = (float)bl / maxp;
for(i=0;i<24;i++) {
printf("%02d-: %.*s\n",i,(int)(scale*p[i]),bar);
}
}
}